import { CurrentUser } from '@/decorators';
import {
  CollectionCreateDto,
  CollectionFindDto,
  CollectionListDto,
  CollectionOrderDto,
  CollectionOrderQueryDto,
  CollectionOrderReportDto,
  CollectionQueryDto,
  CollectionReceiveDto,
  CollectionUpdateDto,
} from '@/dtos';
import { InvoiceOrderStatusEnum } from '@/enums';
import { CollectionService } from '@/services/collection.service';
import { halt, ok } from '@/utils';
import { Body, Controller, Get, Param, Patch, Post } from '@nestjs/common';
import { Throttle, seconds } from '@nestjs/throttler';
import { random } from 'radash';
import { BaseController } from './base.controller';

@Controller('collection')
export class CollectionController extends BaseController {
  constructor(private readonly collectionService: CollectionService) {
    super();
  }

  @Post('order')
  @Throttle({
    short: {
      ttl: seconds(1),
      limit: 1,
    },
  })
  async order(
    @Body() dto: CollectionOrderDto,
    @CurrentUser('id') userId: number,
  ) {
    return ok(await this.collectionService.order(dto, userId));
  }

  @Post('list')
  async list(@Body() dto: CollectionListDto) {
    return ok(await this.collectionService.list(dto));
  }

  @Get('detail/:id')
  async getDetail(@Param('id') id: number, @CurrentUser('id') userId: number) {
    const data = (await this.collectionService.findOne(
      {
        id,
        isDeleted: false,
      },
      {
        id: true,
        title: true,
        image: true,
        limit: true,
        price: true,
      },
    )) as any;
    if (!data) {
      return halt('数据异常');
    }
    const hold = await this.prisma.userHoldCollection.count({
      where: {
        collectionId: id,
        userId,
      },
    });
    const invoice = await this.prisma.invoiceOrder.findFirst({
      where: {
        collectionId: id,
        ownerId: userId,
      },
    });
    return ok({
      ...data,
      price: Number(data?.price),
      hold: Number(hold),
      isReceive: !!invoice,
      isDispatched: invoice
        ? invoice.status === InvoiceOrderStatusEnum.COMPLETED
        : 0,
      express: invoice?.express ?? {},
    });
  }

  @Post('sign')
  @Throttle({
    short: {
      ttl: seconds(1),
      limit: 1,
    },
  })
  async sign(@CurrentUser('id') id: number) {
    const user = await this.prisma.user.findUnique({ where: { id } });
    if (!user.isReal) {
      return halt('请先完成实名认证');
    }
    let camp = null;
    if (!user?.camp || user?.camp <= 0) {
      camp = random(1, 18);
      await this.prisma.user.update({
        where: {
          id,
        },
        data: {
          camp,
        },
      });
    } else {
      camp = user.camp;
    }

    return ok({
      camp,
    });
  }

  @Post('receive')
  @Throttle({
    short: {
      ttl: seconds(1),
      limit: 1,
    },
  })
  async receive(
    @Body() dto: CollectionReceiveDto,
    @CurrentUser('id') id: number,
  ) {
    return ok(await this.collectionService.receive(dto, id));
  }

  @Post('admin/report')
  async report(@Body() { params, sort }: CollectionOrderReportDto) {
    const { current = 1, pageSize = 20, keyword, ...where } = params;

    let sql = `
      SELECT #{fields} FROM \`UserHoldCollection\` AS t
      LEFT JOIN \`Collection\` AS c ON t.collectionId = c.id    
      WHERE c.isDeleted = false
        ${!!where.startTime ? `AND CONVERT_TZ(t.createdAt, 'UTC', 'Asia/Shanghai') >= '${where.startTime}'` : ''}
        ${!!where.endTime ? `AND CONVERT_TZ(t.createdAt, 'UTC', 'Asia/Shanghai') <= '${where.endTime}'` : ''}
        ${!!where.collectionId ? `AND t.collectionId = ${where.collectionId}` : ''}
        ${!!where.title ? `AND c.title LIKE '%${where.title}%'` : ''}
      GROUP BY t.collectionId
    `;

    const res = await this.prisma.$queryRawUnsafe<[{ count: number }]>(
      sql.replace(
        '#{fields}',
        `
            COUNT(t.collectionId) AS count
        `,
      ),
    );

    const list: any[] = await this.prisma.$queryRawUnsafe(
      `${sql.replace(
        '#{fields}',
        `
            t.collectionId, c.title,
            COUNT(t.collectionId) AS count,
            SUM(t.total) AS amount
        `,
      )}
      ORDER BY ${
        !!sort && Object.keys(sort).length > 0
          ? Object.entries(sort)
              .map(([k, v]) => {
                if (['count', 'amount'].includes(k)) {
                  return `${k} ${v === 'descend' ? 'DESC' : 'ASC'}`;
                }
                return `t.${k} ${v === 'descend' ? 'DESC' : 'ASC'}`;
              })
              .join(',')
          : 't.collectionId DESC'
      }
      LIMIT ${pageSize}
      OFFSET ${(current - 1) * pageSize}
      `,
    );

    const total = res ? res.length : 0;
    const totalPage = Math.ceil(total / pageSize);
    const hasMore = current < totalPage;

    return ok({
      total,
      totalPage,
      hasMore,
      current,
      pageSize,
      list: list?.map((item) => ({
        ...item,
        count: Number(item.count),
        amount: Number(item.amount),
      })),
    });
  }

  @Post('admin/orders')
  async orders(@Body() dto: CollectionOrderQueryDto) {
    return ok(await this.collectionService.orders(dto));
  }

  @Post('admin/query')
  async query(@Body() dto: CollectionQueryDto) {
    return ok(await this.collectionService.query(dto));
  }

  @Post('admin/find')
  async find(@Body() dto: CollectionFindDto) {
    return ok(await this.collectionService.findOne(dto.where, dto.select));
  }

  @Post('admin/create')
  async create(@Body() dto: CollectionCreateDto) {
    return ok(await this.collectionService.create(dto.data));
  }

  @Patch('admin/update')
  async update(@Body() dto: CollectionUpdateDto) {
    return ok(await this.collectionService.update(dto));
  }
}
